import express from 'express';
import {query} from "./engine/query.js";
import {getCount} from './engine/flightSearch.js'
import {log} from "./utils/log.js";
import assert from 'assert'
import dayjs from 'dayjs'

import {TimeoutError, ValidParamError} from "./utils/errors.js"
import {verifiyEfficiency, incrRequestCount, addTraceChannels} from "./vm/monitor.js";
import {padZero} from "./utils/utils.js";
import {genBgr, reloadVM} from "./vm/out.js";

import v8 from "v8";

function performGarbageCollection() {
    if (typeof global.gc === 'function') {
        global.gc();
    } else {
        console.error('Global gc function is not exposed.');
    }
}

// 手动触发垃圾回收
export const metaRouter = express.Router();

metaRouter.get('/ping', (req, res) => {
    res.send('Pong!');
})
metaRouter.get('/reload', async (req, res) => {
    req.log.info('reload vm');
    let r = reloadVM();
    req.log.info("reload vm end");
    res.send({'code': true});
})
metaRouter.get('/heapdump', async (req, res) => {
    req.log.info('reload vm');
    req.log.info("reload vm end");
    res.send({'code': true});
})
metaRouter.get('/gc', async (req, res) => {
    req.log.info('gc');
    performGarbageCollection();
    req.log.info("gc end");
    res.send({'code': true});
})
metaRouter.get('/reload2', async (req, res) => {
    req.log.info('reload vm');
    let r = reloadVM();
    req.log.info("reload vm end");
    for (let i = 0; i < 10000; i++) {
        for (let j = 0; j < 3; j++) {
            try {
                let r = await genBgr('asdjkahskjdha')
                req.log.info(r)
            } catch (e) {
                // req.log.error(e);
            }
        }
    }
    res.send({'code': true});
})

/**
 *
 * @param {requestParam} params
 * @param {Logger} log
 */
function validParams(params, log) {
    // 处理出发日期
    let startDayObj = dayjs()
    let startDateMonth = parseInt(params.date.substring(0, 2), 10);

    assert(startDateMonth > 0 && startDateMonth <= 12, "require valid departure month");

    let startDateDay = parseInt(params.date.substring(3, 5), 10);
    let now = new Date();
    let date = new Date();
    let nowMonth = now.getMonth() + 1;
    let nowDate = now.getDate();
    let nowYear = now.getFullYear();
    if (startDateMonth < nowMonth || (nowMonth >= startDateMonth &&  startDateDay< nowDate)) {
        nowYear += 1;
    }
    params.formatDate = `${nowYear}-${padZero(startDateMonth)}-${padZero(startDateDay)}`;
    log.info(`convert from date ${params.date} to ${params.formatDate}`);
    assert(date.getTime() >= now.getTime(), new ValidParamError("date < now"));
    if (params.isTripType) {
        let endDateMonth = parseInt(params.dateEnd.substring(0, 2), 10);

        assert(endDateMonth > 0 && endDateMonth <= 12, "require valid return month");

        let endDateDay = parseInt(params.dateEnd.substring(3, 5), 10);
        let now = new Date();
        let dateEnd = new Date();
        let nowMonth = now.getMonth() + 1;
        let nowDate = now.getDate();
        let nowYear = now.getFullYear();
        if (endDateMonth < nowMonth || (nowMonth >= endDateMonth && endDateDay < nowDate )) {
            nowYear += 1;
        }
        params.formatDateEnd = `${nowYear}-${padZero(endDateMonth)}-${padZero(endDateDay)}`;
        log.info(`convert end date ${params.dateEnd} to ${params.formatDateEnd}`);
        if (dayjs(params.formatDate).isAfter(dayjs(params.formatDateEnd))){
            throw new ValidParamError("date < dataEnd")
        }

    }
    if (!dayjs(params.formatDate).format('YYYY-MM-DD') === params.formatDate
    || !dayjs(params.formatDateEnd).format('YYYY-MM-DD') === params.formatDateEnd){
        throw new ValidParamError(`date format error  ${params.formatDate} ${params.formatDateEnd}`)
    }

    // 判断其他参数
    assert(params.currencyName !== "", new ValidParamError("require nonnull currency"));
    assert(params.startCity !== "", new ValidParamError("require nonnull originCityCode"));

    assert(params.formatDate !== "", new ValidParamError("require nonnull departureDate"));
    assert(params.adultSum !== null, new ValidParamError("require nonnull adultSum"));
    assert(params.cabinClass !== '', new ValidParamError("require nonnull cabinClass"));
    if (params.isTripType) {
        assert(params.endCity !== "", new ValidParamError("require nonnull destinationCityCode"));
    }

    // 判断 cheapest 参数
    if (params.cheapest === undefined){
        params.cheapest = false;
    }

}

/**
 * @param {requestParam} json
 */
metaRouter.post('/query', async (req, res) => {
    let result = null;
    let clearTimeId = null;
    try {
        let startTime = Date.now();
        // incrRequestCount()
        let json = req.body;
        validParams(json, req.log);
        result = await query(req.body, req.log);
        clearTimeId = result.abortId;
        delete result.abortId;
        req.log.info(`rt:${json.isTripType?1:0} request end channels: ${result.channels.length} time cost ${(Date.now() - startTime)/1000}s  ${result.channels.join("_")}`);
        req.log.info(`finally functionCallCount : `,result.functionCallCount);
        // addTraceChannels(req.traceId, result.channels)
        // verifiyEfficiency(req.log)
        if (result.channels.length === 0) {
            throw Error('no channel found')
        }
        // let result = {
        //     'channels': ['ly.com']
        // }
        res.send({
            code: 0,
            success: true,
            message: 'success',
            data: result
        });
        if (clearTimeId !== null && clearTimeId !== undefined) {
            clearTimeout(clearTimeId)
        }
        return
    } catch (err) {
        if (err instanceof TimeoutError && result != null && result.channels.length !== 0) {
            res.send({
                code: 0,
                success: true,
                message: 'success',
                data: result
            });
            return
        }
        req.log.error(err.stack);
        req.log.error('error', err.message)
        res.send({
            code: -1,
            message: err.message,
            success: false
        });
        if (clearTimeId !== null && clearTimeId !== undefined) {
            clearTimeout(clearTimeId)
        }
        return
    } finally {
        if (clearTimeId !== null && clearTimeId !== undefined) {
            clearTimeout(clearTimeId)
        }

    }
})

